#include "ServerHandler.h"
#include "ScreenHandler.h"
#include "WorldHandler.h"
#include "NetworkPacket.h"
#include "Logging.h"
#include <sstream>

#ifdef _WIN32
	#include <windows.h>
#else
	//leenoks
	#include <unistd.h>
#endif

#define COMPASSRANGE 50

ServerHandler::ServerHandler()
{

}

#if 0
void ServerHandler::waitLoop()
{
	for(unsigned int i = 1; /*!stopRequested*/ ; ++i)
	{
		if(35 <= i)
		{
			handleReceivedMessages();
			sendMessages();
			i = 1;
		}
		else
		{
			USLEEP(1);
		}
	}
}
#endif

void ServerHandler::handleReceivedMessages()
{
	// Vector of packets (with Serveral packet elements) paired with their size_t clientCount = ;
	for(size_t i = 0; i < receivedQueue.size(); ++i)
	{
		NetworkPacket* packet = &receivedQueue.at(i).first;
		ClientAddress clientAddress = receivedQueue.at(i).second;
		HeaderType packetHeader = packet->first;
		
		std::stringstream portSS;
		portSS << clientAddress.port;
		
		std::string clientPlayerName = "";
		if(packetHeader != INIT)
		{
			for(std::map<std::string, ClientAddress>::iterator it = clientAddressMap.begin(); it != clientAddressMap.end(); ++it)
			{
				if(static_cast<ClientAddress>(it->second).ip == clientAddress.ip
				&& static_cast<ClientAddress>(it->second).port == clientAddress.port)
				{
					clientPlayerName = static_cast<std::string>(it->first);
				}
			}
		}

		if(packetHeader == INIT
		|| clientPlayerName != "") //if clientPlayerName was not set then the data received was not from a connected client
		{
			std::string source = clientAddress.ip + ":" + portSS.str();
			switch(packetHeader) //HEADER
			{
				case INIT:
					logMessage("Received INIT from " + source + ".");
					handleReceivedInit(packet->second, &clientAddress);
				break;
				case DISC:
					logMessage("Received DISC from " + clientPlayerName + "@" + source + ".");
					handleReceivedDisc(clientPlayerName, &clientAddress);
				break;
				case CHATMESSAGE:
					logMessage("Received CHATMESSAGE from " + clientPlayerName + "@" + source + ".");
					handleReceivedChatMessage(clientPlayerName, packet->second, &clientAddress);
				break;
				case PLAYEROBJECT:
		//			logMessage("Received PLAYEROBJECT from " + clientPlayerName + "@" + source + ".");
					handleReceivedPlayerObject(packet->second, &clientAddress);
				break;
				case REQUESTSEEDANDTP:
					logMessage("Received REQUESTSEED from " + clientPlayerName + "@" + source + ".");
					//if(globalWorldHandler.getRandomSeed() == NO_SEED)
					handleReceivedRequestSeedAndTP();
				break;
				case REQUESTDISMANTLE:
					logMessage("Received REQUESTDISMANTLE from " + clientPlayerName + "@" + source + ".");
					handleReceivedRequestDismantle();
				break;
				case ATTACKOBJECT:
					logMessage("Received ATTACKOBJECT from " + clientPlayerName + "@" + source + ".");
					handleReceivedAttackObject(clientPlayerName, packet->second);
				break;
				case REQUESTRESS:
					logMessage("Received REQUESTRESS from " + clientPlayerName + "@" + source + ".");
					handleReceivedRequestRess(clientPlayerName);
				break;
			}
		}
	}

	if(receivedQueue.size() > 0)
	{
		globalCSHandler->setHasConnection(true);
	}
	
	receivedQueue.clear();
}

void ServerHandler::handleReceivedInit(const std::string& name, ClientAddress* clientAddress)
{
	std::map<std::string, PlayerObject*>::iterator it1 = clientPlayerObjectMap.find(name);

	bool success = false;
	
	if(it1 == clientPlayerObjectMap.end()) //Then player is connecting for the first time
	{
		PlayerObject* newPlayerObject = globalWorldHandler.setupNewPlayer(name, core::vector3df(0, 0, 0));
		clientPlayerObjectMap.insert(std::pair<std::string, PlayerObject*>(name, newPlayerObject));

		clientAddressMap.insert(std::pair<std::string, ClientAddress>(name, *clientAddress));;
		globalScreenHandler.appendToChatLog("[SERVER]: New player " + name + " connected.\n");

		success = true;
	}
	else
	{
		//logMessage("Player " + name + " reconnected.");
		
		PlayerObject* playerObject = static_cast<PlayerObject*>(it1->second);
		std::map<std::string, ClientAddress>::iterator it2 = clientAddressMap.find(name);
		
		if(it2 != clientAddressMap.end()) //No client should exist with name
		{
			clientAddressMap.erase(it2);
		}
		
		reinterpret_cast<scene::ISceneNode*>(playerObject->getNode())->setVisible(true);
		playerObject->setMovementInfo(playerObject->getPosition(), core::vector3df(0, 0, 0), core::vector3df(0, 0, 0), 0, 0);
		playerObject->setIsConnected(true);
			
		clientAddressMap.insert(std::pair<std::string, ClientAddress>(name, *clientAddress));

		globalScreenHandler.appendToChatLog("[SERVER]: Player " + name + " reconnected.\n");
		
		if(globalWorldHandler.getActiveRandomZoneObject() != NULL
		&& playerObject->getIsInTown() == false)
		{
			std::stringstream ss;
			std::string seedString;
			ss << globalWorldHandler.getRandomSeed();
			ss >> seedString;
			globalCSHandler->addToSendQueue(reinterpret_cast<uintptr_t>(&seedString), GRANTSEEDANDTP);
		}
		
		success = true;
	}

	if(success == true)
	{
		std::string chatLog = globalScreenHandler.getChatLog();
		globalCSHandler->addToSendQueue(reinterpret_cast<uintptr_t>(&chatLog), CHATLOG);
	}
}

void ServerHandler::handleReceivedDisc(const std::string& name, ClientAddress* clientAddress)
{
	std::map<std::string, ClientAddress>::iterator it1 = clientAddressMap.find(name);
	
	if(it1 != clientAddressMap.end())
	{
		clientAddressMap.erase(it1);
		
		std::map<std::string, PlayerObject*>::iterator it2 = clientPlayerObjectMap.find(name);
		PlayerObject* playerObject = static_cast<PlayerObject*>(it2->second);

		playerObject->setStartMoving(false);
		playerObject->setIsMoving(false);
		reinterpret_cast<scene::ISceneNode*>(playerObject->getNode())->setVisible(false);

		playerObject->setMovementInfo(playerObject->getPosition(), core::vector3df(-1, -1, -1), core::vector3df(0, 0, 0), 0, 0);
		playerObject->setIsConnected(false);

		std::string tempName = name;
		globalCSHandler->addToSendQueue(reinterpret_cast<uintptr_t>(&tempName), DISC);

		globalScreenHandler.appendToChatLog("[SERVER]: Player " + name + " disconnected.\n");
	}
}

void ServerHandler::handleReceivedChatMessage(const std::string& name, const std::string& message, ClientAddress* clientAddress)
{
	globalScreenHandler.appendToChatLog(name + ": " + message + "\n"); //FIXME: Put real value instead of 1
}

void ServerHandler::handleReceivedPlayerObject(const std::string& senderPobj, ClientAddress* clientAddress)
{
	PlayerObject newPlayerObject; 
	if(serializer.callDeserialize(reinterpret_cast<uintptr_t>(&newPlayerObject), PLAYEROBJECT, senderPobj) == false)
	{
		return;
	}
	
	std::string name = newPlayerObject.getName();
	
	std::map<std::string, PlayerObject*>::iterator it1 = clientPlayerObjectMap.find(name);	

	if(it1 != clientPlayerObjectMap.end())
	{
		std::map<std::string, ClientAddress>::iterator it2 = clientAddressMap.find(name);
		
		if(it2 != clientAddressMap.end())
		{
			MovementInfo newMovementInfo;
			PlayerObject* oldPlayerObject = static_cast<PlayerObject*>(it1->second);
			newMovementInfo = *newPlayerObject.getMovementInfo();
			
			core::vector3df oldPosition = oldPlayerObject->getPosition();
			core::vector3df newPosition = newPlayerObject.getPosition();		
			
			core::vector3df distance = newPosition - oldPosition;
			if(sqrt(distance.X*distance.X) > 20
			|| sqrt(distance.Z*distance.Z) > 20
			&& !(sqrt(distance.X*distance.X) > 2500 || sqrt(distance.Z*distance.Z) > 2500))
			{
				oldPlayerObject->setPosition(newPosition);
				newMovementInfo.startPos = newPosition;
				oldPlayerObject->setIsMoving(false);
				oldPlayerObject->setStartMoving(false);
			}
			
			if(newMovementInfo.destinationPos != oldPlayerObject->getMovementInfo()->destinationPos
			&& !(sqrt(distance.X*distance.X) > 2500 || sqrt(distance.Z*distance.Z) > 2500))
			{
				oldPlayerObject->setMovementInfo(newMovementInfo.startPos, newMovementInfo.destinationPos, core::vector3df(0, 0, 0), 0, 0);
				oldPlayerObject->setStartMoving(true);
			}
		}
	}
}

void ServerHandler::prepareSendQueue()
{
	std::vector<NetworkPacket> packetBundle;

	/* Enemies */
	std::vector<EnemyObject*>enemyObjectsPointers = globalWorldHandler.getEnemyObjects();		
	std::vector<EnemyObject> enemyObjects;
	for(size_t i = 0; i < enemyObjectsPointers.size(); ++i)
	{
		EnemyObject enemyObject = *enemyObjectsPointers[i];
		enemyObjects.push_back(enemyObject);
	}
	packetBundle.push_back(std::make_pair(ENEMYOBJECTVECTOR, serializer.callSerialize(reinterpret_cast<uintptr_t>(&enemyObjects), ENEMYOBJECTVECTOR)));
	
	/* Attacks */
	std::vector<AttackObject*>projectileObjectsPointers = globalWorldHandler.getAttackObjects();		
	std::vector<AttackObject> projectileObjects;
	for(size_t i = 0; i < projectileObjectsPointers.size(); ++i)
	{
		AttackObject projectileObject = *projectileObjectsPointers[i];
		projectileObjects.push_back(projectileObject);
	}
	packetBundle.push_back(std::make_pair(ATTACKOBJECTVECTOR, serializer.callSerialize(reinterpret_cast<uintptr_t>(&projectileObjects), ATTACKOBJECTVECTOR)));
	
	/* Chat log */
	static std::string previousChatLog;
	const std::string& chatLog = globalScreenHandler.getChatLog();
	if(chatLog != previousChatLog)
	{
		if(chatLog.length() > 0)
		{
		    packetBundle.push_back(std::make_pair(CHATLOG, chatLog));
		}
	}
	previousChatLog = chatLog;

	/* Players */
	std::vector<PlayerObject*>playerObjectsPointers = globalWorldHandler.getPlayerObjects();		
	std::vector<PlayerObject> playerObjects;
	for(size_t i = 0; i < playerObjectsPointers.size(); ++i)
	{
		PlayerObject playerObject = *playerObjectsPointers[i];
		playerObjects.push_back(playerObject);
	}
	packetBundle.push_back(std::make_pair(PLAYEROBJECTVECTOR, serializer.callSerialize(reinterpret_cast<uintptr_t>(&playerObjects), PLAYEROBJECTVECTOR)));

	sendQueue.insert(sendQueue.end(), packetBundle.begin(), packetBundle.end());
}

void ServerHandler::sendMessages()
{
	if(sendQueue.size() > 0)
	{
		for(std::map<std::string, ClientAddress>::iterator it = clientAddressMap.begin(); it != clientAddressMap.end(); ++it)
		{
			std::string serializedData = serializer.callSerialize(reinterpret_cast<uintptr_t>(&sendQueue), PACKETBUNDLE);
			socketCommunicator.sendAsync(&it->second, serializedData);
		}
	}
}

void ServerHandler::handleReceivedRequestSeedAndTP()
{
	if(globalWorldHandler.getActiveRandomZoneObject() == NULL)
	{
		bool grantTP = true;
		std::vector<PlayerObject*> playerObjects = globalWorldHandler.getPlayerObjects();
		core::vector3df compassObjectPos = globalWorldHandler.getCompassObject()->getPosition();
		for(unsigned int i = 0; i < playerObjects.size(); ++i)
		{
			PlayerObject* playerObject = playerObjects.at(i);
		   	f32 diffX = playerObject->getPosition().X - compassObjectPos.X;
			f32 diffZ = playerObject->getPosition().Z - compassObjectPos.Z;
			
			if(playerObject->getIsConnected() == false)
			{
				continue;
			}
			
			if(sqrt(diffX * diffX) > COMPASSRANGE
			|| sqrt(diffZ * diffZ) > COMPASSRANGE)
			{
				grantTP = false;
			}
		}

		if(grantTP == true)
		{
			unsigned int seed = rand()%65533+2;

			std::stringstream ss;
			std::string seedString;
			ss << seed;
			ss >> seedString;
			addToSendQueue(reinterpret_cast<uintptr_t>(&seedString), GRANTSEEDANDTP);

			globalWorldHandler.setRandomSeed(seed); // +2 to skip 0[NO_SEED] and 1[WANT_SERVERSEED]
			globalWorldHandler.setupRandomZone();
		}
	}	
}

void ServerHandler::handleReceivedRequestDismantle()
{
	if(globalWorldHandler.getActiveRandomZoneObject() != NULL
	&& globalWorldHandler.getIsCompassObjectActive() == true)
	{
		bool grantTP = true;
		std::vector<PlayerObject*> playerObjects = globalWorldHandler.getPlayerObjects();
		core::vector3df compassObjectPos = globalWorldHandler.getCompassObject()->getPosition();
		for(unsigned int i = 0; i < playerObjects.size(); ++i)
		{
			PlayerObject* playerObject = playerObjects.at(i);
		   	f32 diffX = playerObject->getPosition().X - compassObjectPos.X;
			f32 diffZ = playerObject->getPosition().Z - compassObjectPos.Z;

			if(playerObject->getIsConnected() == false
			|| playerObject->getIsDead() == true
			|| playerObject->getIsInTown() == true)
			{
				continue;
			}
			
			if(sqrt(diffX * diffX) > COMPASSRANGE
			&& sqrt(diffZ * diffZ) > COMPASSRANGE)
			{
				grantTP = false;
			}
		}

		if(grantTP == true)
		{
			globalWorldHandler.dismantleRandomZone();
			globalWorldHandler.setRandomSeed(0);
			std::string temp = "";
			addToSendQueue(reinterpret_cast<uintptr_t>(&temp), GRANTDISMANTLE);
		}
	}
}

void ServerHandler::handleReceivedAttackObject(const std::string& name, const std::string& senderObj)
{
//	std::cout << "QATTACKOBJECT BEFORE SERIALIZEQ" << senderObj << "QQ\n";
	std::map<std::string, PlayerObject*>::iterator it = clientPlayerObjectMap.find(name);
	if(it != clientPlayerObjectMap.end())
	{
		AttackObject newAttackObject;
		if(serializer.callDeserialize(reinterpret_cast<uintptr_t>(&newAttackObject), ATTACKOBJECT, senderObj) == false)
		{
			return;
		}
		//TODO: regard if legit
		core::vector3df startPos = newAttackObject.getPosition();
		core::matrix4 mat = newAttackObject.getMat();
		AttackType type = newAttackObject.getAttackType();


		std::map<std::string, PlayerObject*>::iterator it = clientPlayerObjectMap.find(name);
		if(it != clientPlayerObjectMap.end())
		{
			PlayerObject* playerObject = static_cast<PlayerObject*>(it->second);

			playerObject->setAnimation(scene::EMAT_ATTACK, false);
			MovementInfo* oldMovementInfo = playerObject->getMovementInfo();
			playerObject->setMovementInfo(startPos, oldMovementInfo->destinationPos, core::vector3df(0, 0, 0), 0, 0);
			playerObject->setPosition(startPos);
			playerObject->setIsMoving(false);
			playerObject->setStartMoving(false);
			playerObject->faceTarget(playerObject->getPosition() + core::vector3df(mat[0],mat[1],mat[2]));
		}
		
		globalWorldHandler.setupAttack(startPos, mat, type, true, name);
	}
}

void ServerHandler::handleReceivedRequestRess(const std::string& name)
{
	std::map<std::string, PlayerObject*>::iterator it = clientPlayerObjectMap.find(name);

	//TODO: regard if legit
	
	if(it != clientPlayerObjectMap.end())
	{
		static_cast<PlayerObject*>(it->second)->setIsDead(false);
	}
}
